package v2;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Random;
/**
 * 
 */
import java.util.HashMap;

/**
 * @author bosc & dunoyerd
 * @version 0.1
 * @see
 * @since version 0.1
 */
public class Animaux extends Entite {

	// Attributs

	/**
	 * La jauge de nourriture de l'animal.
	 * 
	 * @see Entite#Entite(int,int,Position,int,int,int,int,Color,int,int)
	 * @see Animaux#Animaux(int,int,Position,int,int,int,int,Color,int,int,int,int,boolean,boolean,boolean,int,String,HashMap,HashMap)
	 */
	private int jaugeNourriture;

	/**
	 * L'animal est un male
	 * 
	 * @see Entite#Entite(int,int,Position,int,int,int,int,Color,int,int)
	 * @see Animaux#Animaux(int,int,Position,int,int,int,int,Color,int,int,int,int,boolean,boolean,boolean,int,String,HashMap,HashMap)
	 */
	private boolean male;

	/**
	 * Le type d'animal de l'animal
	 * 
	 * @see Entite#Entite(int,int,Position,int,int,int,int,Color,int,int)
	 * @see Animaux#Animaux(int,int,Position,int,int,int,int,Color,int,int,int,int,boolean,boolean,boolean,int,String,HashMap,HashMap)
	 */
	private TypeAnimal espece;

	// Constructeurs

	/**
	 * Constructeur d'Animaux
	 */
	public Animaux(int maSante, int maJaugeEau, Position maPosition, int maJaugeNourriture,  boolean monMale, TypeAnimal monEspece)
	{		
		super(maSante,maJaugeEau,maPosition,
				monEspece.maxEau(), monEspece.minEau(), monEspece.maxSante(),
				monEspece.facteurPerteEau(), monEspece.Couleur(), monEspece.gainSante(),
				monEspece.dureeVieInit());

		// Test maximum nourriture
		if (maJaugeNourriture>monEspece.maxNourriture())
		{
			this.jaugeNourriture = monEspece.maxNourriture();
		}
		else
		{
			this.jaugeNourriture = maJaugeNourriture;
		}
		this.male = monMale;
		this.espece = monEspece;
	}

	// Accesseurs
	/**
	 * Retourne la jauge de nourriture
	 */
	public int obtientJaugeNourriture() {
		return jaugeNourriture;
	}

	/**
	 * Retourne la valeur de nourriture maximale
	 * 
	 * @return la valeur de nourriture maximale
	 */
	public int obtientMaxNourriture() {
		return espece.maxNourriture();
	}

	/**
	 * Retourne la valeur de nourriture minimale
	 * 
	 * @return la valeur de nourriture minimale
	 */
	public int obtientMinNourriture() {
		return espece.minNourriture();
	}

	/**
	 * Détermine si mon animal est un mâle
	 * 
	 * @return True si c'est un mâme, flase sinon
	 */
	public boolean estMale() {
		return male;
	}

	/**
	 * Retourne l'espece de l'animal
	 * 
	 * @return une enum correpsondant à l'éspece
	 */
	public TypeAnimal obtientTypeAnimal() {
		return espece;
	}

	/**
	 * Permet de changer la valeur de la jauge de nourriture
	 * 
	 * @param jaugeNourriture la nouvelle valeur de la jauge de nourriture
	 */
	public void assigneJaugeNourriture(int jaugeNourriture) {
		this.jaugeNourriture = jaugeNourriture;
	}


	//////////////
	// Méthodes //
	//////////////

	/**
	 * Determine ce que l'animal désire faire
	 * 
	 * 0: par defaut (fuite possible)
	 * 1: nourriture absolument
	 * 2: nourriture (fuite possible)
	 * 3: eau absolument
	 * 4: eau (fuite possible)
	 * 
	 * @return le code d'action du tour
	 */
	public int calculRaisonDeplacement()
	{
		int raisonDeplacement = 0;

		if (obtientJaugeNourriture()< (obtientMaxNourriture()+obtientMinNourriture())/2)
		{
			if (obtientJaugeNourriture()< obtientMinNourriture())
			{
				raisonDeplacement=1;
			}
			else
			{
				raisonDeplacement=2;
			}
		}
		if (obtientJaugeEau()< (obtientMaxEau()+obtientMinEau())/2)
		{
			if (obtientJaugeEau()< obtientMinEau())
			{
				raisonDeplacement = 3;
			}
			else if (raisonDeplacement != 1)
			{
				raisonDeplacement = 4;
			}			
		}

		return raisonDeplacement;
	}



	/**
	 * Retourne la liste de predateurs dans le champ de visions. Sont considéré comme prédateurs les animaux étant référencés comme tel par this...
	 * 
	 * @param mesParcelles la liste des parcelles dans le champs de vision
	 * @return les predateurs dans le champs de vision
	 */
	public ArrayList<Animaux> rechercheMesPredateurs(ArrayList<Parcelle> mesParcelles)
	{
		ArrayList<Animaux> mesPredateurs = new ArrayList<Animaux>();

		if (!this.obtientTypeAnimal().proie())
		{
			return null;
		}

		for (int i =0;i<mesParcelles.size();i++)
		{
			Parcelle maParcelleTemp = mesParcelles.get(i);

			// presence animal
			if (maParcelleTemp.obtientMonAnimal() != null)
			{
				// Verifie les animaux
				Animaux monAnimal = maParcelleTemp.obtientMonAnimal();

				// proie/predateur + especes différentes + est predateur connu
				if (this.obtientTypeAnimal().proie() && monAnimal.obtientTypeAnimal().predateur() && (monAnimal.obtientTypeAnimal() != this.obtientTypeAnimal()) && this.espece.predateurs().containsValue(monAnimal.espece.monEspece()))
				{ 
					mesPredateurs.add(monAnimal);
				}
			}
		}
		return mesPredateurs;
	}

	/**
	 * Retourne la liste des parcelles dans le champ de vision
	 * 
	 * @param mesParcelles les parcelles dans le champ de vision
	 * @return la liste des partenaires dans le champ de vision
	 */
	public ArrayList<Animaux> rechercheMesPartenaires(ArrayList<Parcelle> mesParcelles)
	{
		ArrayList<Animaux> mesPartenaires = new ArrayList<Animaux>();

		for (int i = 0; i<mesParcelles.size(); i++)
		{
			Parcelle maParcelleTemp = mesParcelles.get(i);

			// présence animal
			if (maParcelleTemp.obtientMonAnimal() != null)
			{
				// Verifie les animaux
				Animaux monAnimal = maParcelleTemp.obtientMonAnimal();

				// même espece et sexe différent
				if ((monAnimal.obtientTypeAnimal() == this.obtientTypeAnimal()) && (monAnimal.estMale() != this.estMale()))
				{
					mesPartenaires.add(monAnimal);
				}
			}
		}
		return mesPartenaires;
	}

	/**
	 * Trouve et renvoie le plus proche partenaire.
	 * 
	 * @param mesPartenaires la liste des partenaires dans le champ de vision
	 * @return la partenaire le plus proche ou null si il n'y en a pas
	 */
	public Animaux recherchePlusProchePartenaire(ArrayList<Animaux> mesPartenaires)
	{
		if (mesPartenaires.isEmpty())
		{
			return null;
		}

		// calcul de la premiere valeur
		Animaux monPartenaire = mesPartenaires.get(0);
		double maDistance = Position.calculDistance(this.obtientPosition(),monPartenaire.obtientPosition());

		for(int i =1;i<mesPartenaires.size();i++)
		{
			Animaux monPartTemp = mesPartenaires.get(i);
			// test si la distance est inférieure...
			if (Position.calculDistance(this.obtientPosition(),monPartTemp.obtientPosition()) < maDistance)
			{
				monPartenaire = monPartTemp;
				maDistance = Position.calculDistance(this.obtientPosition(),monPartTemp.obtientPosition());
			}
		}
		return monPartenaire;
	}

	/**
	 * Permet de se deplacer alétoirement sur une parcelle contigue, libre et accessible. Si il n'y en a pas, je reste a la meme place
	 * 
	 * @param mesParcelles la liste de parcelles visibles
	 */
	public void deplacementSansBut(ArrayList<Parcelle> mesParcelles)
	{

		ArrayList<Parcelle> mesParcLibreAccessEtContig = this.parcellesLibresEtAccess(this.parcellesContigues(mesParcelles));

		Random monRand = new Random();

		int nbParcLibreAccessetContig = mesParcLibreAccessEtContig.size();

		if (nbParcLibreAccessetContig > 0)
		{
			Parcelle maParcDepl = mesParcLibreAccessEtContig.get(monRand.nextInt(nbParcLibreAccessetContig));   

			// Deplacement
			this.deplacementParc(maParcDepl);
		}
	}

	/**
	 * Permet de recuperer la liste des parcelles contigues à un animal (mais pas sa propre parcelle...)
	 * 
	 * @param mesParcelles le liste des parcelles visibles par l'animal
	 * @return la liste des parcelles contigues à mon animal
	 */
	public ArrayList<Parcelle> parcellesContigues(ArrayList<Parcelle> mesParcelles)
	{
		ArrayList<Parcelle> mesParcContigues = new ArrayList<Parcelle>();
		for (int i = 0;i<mesParcelles.size();i++)
		{
			Parcelle maParcelleTemp = mesParcelles.get(i);
			if (this.obtientPosition().estContigue(maParcelleTemp.obtientMaPosition()))
			{
				mesParcContigues.add(maParcelleTemp);
			}
		}
		return mesParcContigues;
	}

	/**
	 * REnvoie la liste des parcelles libres et accessibles dans le champ de vision de l'animal
	 * 
	 * @param mesParcelles la liste des parcelles dans mon chmap de vision
	 * @return mes parcelles libres et accessibles
	 */
	public ArrayList<Parcelle> parcellesLibresEtAccess(ArrayList<Parcelle> mesParcelles)
	{
		ArrayList<Parcelle> mesParcLibres = new ArrayList<Parcelle>();
		for(int i =0;i<mesParcelles.size();i++)
		{
			Parcelle maParcelleTemp = mesParcelles.get(i);
			if (maParcelleTemp.estAccessible() && (maParcelleTemp.obtientMonAnimal() == null))
			{
				mesParcLibres.add(maParcelleTemp);
			}
		}
		return mesParcLibres;
	}

	/**
	 * Déplace un animal sur une parcelle passée en entrée
	 * 
	 * @param maParcelle la Parcelle ou l'animal se déplace
	 */
	public void deplacementParc(Parcelle maParcelle)
	{
		maParcelle.assigneMonAnimal(this);
		this.assignePosition(maParcelle.obtientMaPosition());		
	}

	/**
	 * Methode permettant de determiner la parcelle la plus proche du partenaire et se deplacant dessus
	 * 
	 * @param monPartenaire le partenaire vers lequel je me déplace
	 * @param mesParcelles les parcelles dans le champ de vision de mon animal
	 */
	public void deplacementVersPartenaire(Animaux monPartenaire,ArrayList<Parcelle> mesParcelles)
	{
		// je calcul les parcelles libres, contigues et accessibles
		ArrayList<Parcelle> mesParcellesContigLibre = this.parcellesLibresEtAccess(this.parcellesContigues(mesParcelles));
		//je cherche parmi ces parcelle laquelle est la plus proche du partenaire
		Parcelle maParcelle = monPartenaire.laPlusProcheParcelle(mesParcellesContigLibre);
		//si elle existe, je m'y déplace
		if (maParcelle != null)
		{
			this.deplacementParc(maParcelle);
		}
		// si elle n'existe pas, ca veut dire que l'on ne peux pas se déplacer
	}

	/**
	 * Méthode permettan de calculer la plus proche parcelle de mon animal
	 * 
	 * @param mesParcelles la liste des parcelles
	 * @return la parcelle la plus proche
	 */
	public Parcelle laPlusProcheParcelle(ArrayList<Parcelle> mesParcelles)
	{
		if (mesParcelles.size() == 0)
		{
			return null;
		}

		Parcelle maPlusProcheParcelle = mesParcelles.get(0);
		double maPlusCourteDistance =  Position.calculDistance(this.obtientPosition(), maPlusProcheParcelle.obtientMaPosition());

		for (int i =1;i<mesParcelles.size();i++)
		{
			Parcelle maParcelleTemp = mesParcelles.get(i);
			double maDistTemp = Position.calculDistance(this.obtientPosition(), maParcelleTemp.obtientMaPosition());
			if (maDistTemp < maPlusCourteDistance)
			{
				maPlusProcheParcelle = maParcelleTemp;
				maPlusCourteDistance = maDistTemp;
			}
		}
		return maPlusProcheParcelle;
	}

	/**
	 * Méthode générant un nouvel animal et le place sur une parcelle contigues libre et accessible.
	 * 
	 * @param mesParcellesContigLibres
	 */
	public void copuler(ArrayList<Parcelle> mesParcellesContigLibres)
	{
		if(mesParcellesContigLibres.size() != 0)
		{
			Random monRand = new Random();

			Parcelle maParcelle = mesParcellesContigLibres.get(monRand.nextInt(mesParcellesContigLibres.size()));
			Animaux monPetit = new Animaux( (int) (1+ this.obtientMaxSante()/2 + (this.obtientMaxSante()/2)*monRand.nextGaussian()), (int) (this.obtientMinEau()+monRand.nextGaussian()*(this.obtientMaxEau()-this.obtientMinEau())) , maParcelle.obtientMaPosition(),(int) (this.obtientMinNourriture()+monRand.nextGaussian()*(this.obtientMaxNourriture()-this.obtientMinNourriture())) , monRand.nextBoolean(), this.obtientTypeAnimal());
			maParcelle.assigneMonAnimal(monPetit);
		}
	}

	////// FUITE PREDATEUR //////

	/**
	 * Methode permettant de faire fuir un animal devant un ou plusieurs prédateurs
	 * 
	 * @param mesPredateurs la liste des prédateurs à portée
	 * @param mesParcLibAccContig la liste des parcelles accessibles et contigues
	 */
	public void fuitePredateurs(ArrayList<Animaux> mesPredateurs, ArrayList<Parcelle> mesParcLibAccContig)
	{
		ArrayList<Position> mesPos = new ArrayList<Position>();
		// je calcul un position moyenne: en fonction du type de predateur et de la distance, la fuite st plus ou moins importante...
		for (int i=0;i<mesPredateurs.size();i++)
		{
			mesPos.add(this.fuitePredateur(mesPredateurs.get(i)));
		}

		if (mesPos.size() !=0)
		{
			//je calcul une position "moyenne"
			Position maPosMoy = Position.calculPositionMoyenne(mesPos);

			//je calcul la parcelle de fuite
			Parcelle maParcFuite = maPosMoy.calculPositionFuite(mesParcLibAccContig);

			//j'assigne cette position à mon animal (en l'enlevant de sa position actuelle???)
			this.assignePosition(maParcFuite.obtientMaPosition());
		}
		else
		{
			// SINON, je me déplace aléatoirement
			this.deplacementSansBut(mesParcLibAccContig);
		}
	}
	
	/**
	 * Calcul la position de fuite (parcelle) théorique face à un prédateur
	 * 
	 * @param monPredateur le predateur que je cherche a fuir
	 * @return la position théorique de fuite
	 */
	public Position fuitePredateur(Animaux monPredateur)
	{
		// je calcul la position relative de mon predateur...
		Position maPos = Position.calculPositionRelative(this.obtientPosition(), monPredateur.obtientPosition());
		// j'applique le facteur de fuite
		maPos = Position.calculcalculPositionProportionelle(maPos,this.obtientTypeAnimal().obtientFacteurPredateur(monPredateur.obtientTypeAnimal()));
		// J'inverse les données relatives
		return Position.calculPositionInverse(maPos);
	}


	// Methode se nourrir globale
	/**
	 * Méthode global pour que les animaux se nourrissent
	 * 
	 * @param mesParcelles la liste des parcelles a portée de vue
	 */
	public void seNourrir(ArrayList<Parcelle> mesParcelles){
		// PB: gerer herbivore et carnivore
		if (this.obtientTypeAnimal().predateur())
		{
			// si je suis un prédateur
			this.seNourrirAnimal(mesParcelles);
		}
		else
		{
			this.seNourrirVegetal(mesParcelles);
		}
	}

	/**
	 * Méthode permettant aux canivores de se nourrir
	 * 
	 * @param mesParcelles la liste des parcelles à portée de vue
	 */
	public void seNourrirAnimal(ArrayList<Parcelle> mesParcelles)
	{

		ArrayList<Parcelle> mesParcellesProies = this.proiesAPortee(mesParcelles);

		//SINON:
		// 1- je mange ma proie si elle est a portée
		// 2- je choisie une proie (selon sa distance et son facteur de preference) et je me deplace
		// 3- je mes deplace aléatoirement

		// 1 - proies a proximité
		ArrayList<Parcelle> mesParcProiesContig = this.parcellesContigues(mesParcellesProies);

		if (mesParcProiesContig.size() !=0 )
		{
			// je selectionne ma proie a manger en fonction de la preference de l'animal
			Parcelle maParcProie= this.maParcProiePref(mesParcProiesContig);

			// j'ai maintenant ma proie préférée à portée...
			// je la mange !!!
			this.mangeMaProie(maParcProie);
		}

		// 2 - deplacement...

		if (mesParcellesProies.size() !=0)
		{
			// je choisie ma proie préférée: cela depend a la fois de la distance et de la preference de l'animal
			Parcelle maParcProie= this.maParcProiePref(mesParcProiesContig);

			//je me deplace vers ma parcelle
			this.deplParcItineraireVersParc(mesParcelles, maParcProie);
		}
		else
		{
			// 3 - deplacement aléatoirement a la recherche d'une proie
			this.deplacementSansBut(mesParcelles);
		}
	}

	// permet de calculer la plus proche parcelle contigue a mon animal pour se feplacer vers mon Bobjectif
	/**
	 * Methode permettant à un animal de se deplacer vers une parcelle donnée
	 * 
	 * @param mesParcelles les parcelles de mon champs de vision.
	 * @param monObjectif la parcelle cers laquelle je me déplace.
	 */
	public void deplParcItineraireVersParc(ArrayList<Parcelle> mesParcelles, Parcelle monObjectif)
	{
		ArrayList<Parcelle> mesParcellesContig = this.parcellesContigues(mesParcelles);
		ArrayList<Parcelle> mesParcellesContigLibre = this.parcellesLibresEtAccess(mesParcellesContig);
		Parcelle maParcelle = monObjectif.plusProcheParcelle(mesParcellesContigLibre);
		if (maParcelle != null)
		{
			this.deplacementParc(maParcelle);
		}
		// sinon: je n'ai pas de parcelles contigues libres, jene peux pas me deplacer
	}

	/**
	 * Méthode permetttant de manger une proie
	 * NB: on calcul la nourriture assimilé par le predateur en fonction de la santé de la proie (coef 1:1)
	 * NB2: on calcul egalement le gain hydro, avec le coef 1:2
	 * 
	 * 
	 * @param la parcelle ou se trouve la proie
	 */
	public void mangeMaProie(Parcelle MaParcProie)
	{
		// je calcul la quantité de nourriture que je pourrais manger
		int maQuantiteIdeale = this.obtientMaxNourriture()-this.obtientJaugeNourriture();

		// je calcul maintenant la quantité réelle de nourriture
		int maQuantReeleNour=maQuantiteIdeale;
		if (maQuantiteIdeale>MaParcProie.obtientMonAnimal().obtientSante())
		{
			maQuantReeleNour=MaParcProie.obtientMonAnimal().obtientSante();
		}

		// je calcul pour l'hygrométrie...
		double coefPerteSanté = maQuantReeleNour/MaParcProie.obtientMonAnimal().obtientSante();

		//perte d'eau de la proie et gain prédateur...
		int perteEauProie = (int) coefPerteSanté*MaParcProie.obtientMonAnimal().obtientJaugeEau();

		//j'augmente ma nourriture, j'augmente mon hygro, je tue ma proie...

		//predateur --> augmentation
		this.assigneJaugeNourriture(this.obtientJaugeNourriture()+maQuantReeleNour);
		this.assigneJaugeEau(this.obtientJaugeEau()+perteEauProie/2);
		//proie --> mise a mort
		MaParcProie.obtientMonAnimal().assigneSante(0);
		MaParcProie.assigneMonAnimal(null);
	}

	/**
	 * Methode permettant de calculer la distance entre 2 animaux
	 * 
	 * @param Ani l'animal dont je cherche à déterminer la distance
	 * @return la distance
	 */
	public double distanceDeuxAnimaux(Animaux Ani)
	{
		return Position.calculDistance(this.obtientPosition(), Ani.obtientPosition());
	}

	
	/**
	 * Méthode permettant de trouver ma proie préférée, en fonction de la distance
	 * 
	 * @param mesParcProiesContig les parcelles contigues contenant une proie
	 * @return la parcelle contenant ma proie préférée
	 */
	public Parcelle maParcProiePref(ArrayList<Parcelle> mesParcProiesContig)
	{
		if (mesParcProiesContig.size() != 0)
		{
			// je selectionne ma proie a manger en fonction de la preference de l'animal
			Parcelle maParcProie= mesParcProiesContig.get(0);
			// NB: je rajoute 1 pour eviter de multiplier par 0 apres l'arrondi...
			int maPref = this.obtientTypeAnimal().proies().get(maParcProie.obtientMonAnimal().obtientTypeAnimal().name()) / (int) (this.distanceDeuxAnimaux(maParcProie.obtientMonAnimal())+1);

			for(int i  = 1;i<mesParcProiesContig.size();i++)
			{
				int maPrefTemp=this.obtientTypeAnimal().proies().get(mesParcProiesContig.get(i).obtientMonAnimal().obtientTypeAnimal().name()) /  (int) (this.distanceDeuxAnimaux(mesParcProiesContig.get(i).obtientMonAnimal())+1);
				if (maPrefTemp>maPref)
				{
					maPref=maPrefTemp;
					maParcProie= mesParcProiesContig.get(i);
				}
			}
			return  maParcProie;
		}
		return null;
	}

	/**
	 * Méthode permettant de caculer les proies à portée
	 * 
	 * @param mesParcelles les parcelles dans mon champ de vision
	 * @return les parcelles contenant mes proies a portée
	 */
	public ArrayList<Parcelle> proiesAPortee(ArrayList<Parcelle> mesParcelles)
	{	
		ArrayList<Parcelle> mesParcellesRetour = new ArrayList<Parcelle>();

		for(int i=0;i<mesParcelles.size();i++)
		{
			if (mesParcelles.get(i).obtientMonAnimal() != null)
				// si il y a un animal sur la parcelle...
			{
				Animaux monAniParc= mesParcelles.get(i).obtientMonAnimal();
				if (monAniParc.obtientTypeAnimal().proies().containsKey(monAniParc.obtientTypeAnimal().name()))
					// si l'animal est un proie potentielle...
				{
					// j'ajoute sa parcelle dans ma liste de retour
					mesParcellesRetour.add(mesParcelles.get(i));
				}
			}
		}

		// je test le contenue de mon arraylist
		if (mesParcellesRetour.size()==0)
		{
			return null;
		}
		return mesParcellesRetour;
	}

	/**
	 * Methode permettant à un animal de se désalterer
	 * 
	 * @param mesParcelles les parcelles dnas le champs de vision de l'animal
	 */
	public void seDesalterer(ArrayList<Parcelle> mesParcelles)
	{
		// plusieurs cas:
		// 1 - parcelle a proximité
		// 2 - parcelle dans le champs de vision
		// 3 - pas d'eau --> deplacement aléatoire

		// je calcul mes parcelles d'eau
		ArrayList<Parcelle> mesParcEauPortee = this.eauAPortee(mesParcelles);

		//je regarde si il y a une parcelle à proximité...
		ArrayList<Parcelle> mesParcEauContig = this.parcellesContigues(mesParcEauPortee);

		if (mesParcEauContig.size() != 0)
		{
			// 1 - j'ai une parcelle à proximité...
			Random monRand = new Random();
			// si il y en a plusieurs, j'en prend une au hasard
			Parcelle maParcelle = mesParcEauContig.get(monRand.nextInt(mesParcEauContig.size()));
			// je bois
			this.boire(maParcelle);
		}
		else if (mesParcEauPortee.size() != 0)
		{
			// 2 - je me deplace vers la plus proche parcelle d'eau
			// je récupere ma parcelle la plus proche
			Parcelle maParcelle = this.laPlusProcheParcelle(mesParcEauPortee);
			// je me deplace vers elle
			this.deplParcItineraireVersParc(mesParcEauPortee, maParcelle);
		}
		else
		{
			// 3 - je me déplace sans but à la recherche d'eau
			this.deplacementSansBut(this.parcellesContigues(this.parcellesLibresEtAccess(mesParcelles)));
		}
	}

	/**
	 * Methode permettant à un animal de boire sur une parcelle
	 * 
	 * @param maParcelle la parcelle dans laquelle l'animal boit
	 */
	public void boire(Parcelle maParcelle)
	{
		// calcul quantité d'eau que je voudrais boire
		int monEauDesiree = this.obtientMaxEau()-this.obtientJaugeEau();
		// je bois dans ma parcelle
		int maDiminutionEauParcelle;
		if (monEauDesiree > (maParcelle.obtientTauxHygro()-maParcelle.obtientMonType().minHygro()))
		{
			maDiminutionEauParcelle = maParcelle.obtientTauxHygro()-maParcelle.obtientMonType().minHygro(); 
		}
		else
		{
			maDiminutionEauParcelle = monEauDesiree;
		}

		// je diminue mes valeurs de la parcelle et j'augmente la jeauge de l'animal
		maParcelle.assigneTauxHygro(maParcelle.obtientTauxHygro()-maDiminutionEauParcelle);
		this.assigneJaugeEau(this.obtientJaugeEau()+monEauDesiree);
	}


	/**
	 * Méthode permettant de déterminer les parcelles à portée de vue étatn source d'eau
	 * 
	 * @param mesParcelles les parcelles dans le champs de visions
	 * @return les parcelles dans le chmaps de vision ou mon animal peux s'abreuver
	 */
	public ArrayList<Parcelle> eauAPortee(ArrayList<Parcelle> mesParcelles)
	{
		ArrayList<Parcelle> mesParcEau = new ArrayList<Parcelle>();
		// je fais mes test en boucle et j'ajoute...
		for(int i = 0;i<mesParcelles.size();i++)
		{
			Parcelle parcTemp = mesParcelles.get(i);
			//test
			if (parcTemp.obtientMonType().sourceEau())
			{
				mesParcEau.add(parcTemp);
			}
		}
		return mesParcEau;
	}

	/**
	 * Méthode permettant à mon animal herbivore de se nourrir
	 * par raison de temps (et conception), on n'a pas implémanter les preferences de nourriture pour les herbivores...
	 * --> on se nourrit de toute plante a portée.
	 * 
	 * @param mesParcelles mes parcelles à portée de vue
	 */
	public void seNourrirVegetal(ArrayList<Parcelle> mesParcelles){
		// mes salades
		ArrayList<Parcelle> mesParcSalades = new ArrayList<Parcelle>();

		//je recupere mes plantes
		int i = 0;
		while (i < mesParcelles.size())
		{
			if (mesParcelles.get(i).obtientVegetal() != null)
			{
				// Vérifie les Végétaux
				Vegetaux monVegetal = mesParcelles.get(i).obtientVegetal();

				if (this.espece.proies().contains(monVegetal.obtientMonType().nomEspece()))
				{
					mesParcSalades.add(mesParcelles.get(i));
				}
			}
			i++;
		}

		// si mes plantes ne sont pas vides, je les mange (sinon tant pis...)
		if (!mesParcSalades.isEmpty())
		{
			// je cherche la plante la plus proche...
			Parcelle maSalade = laPlusProcheParcelle(mesParcSalades);

			// maintenant je cherche à m'en approcher, mais sans l'atteindre?
			// --> je cherche la parcelle libre la plus proche
			mesParcelles.remove(maSalade);

			// je commence par ne garder que les parcelles directement autour de mon animal
			mesParcelles = this.parcellesContigues(mesParcelles);

			// je ne garde que les parcelles libres et accessibles
			if (!mesParcelles.isEmpty())
			{
				// 2 CAS
				// soit ma parcelle ou se trouve la plante est a portée
				if (mesParcelles.contains(maSalade))
				{
					// je mange la plante...
					this.mangerPlante(maSalade);
				}
				else
				{
					// soit ma parcelle est loin et je me déplace
					// --> Parmi les parcelles contigues libres et accessibles de mon animal, je prend celle qui est la plus proche de la plante!
					mesParcelles = this.parcellesLibresEtAccess(mesParcelles);
					// je calcul la parcelle la plus proche de la plante
					Parcelle maDestination = maSalade.plusProcheParcelle(mesParcelles);
					// je me deplace
					this.deplacementParc(maDestination);
				}
			}
			else
			{
				//: sinon, je me déplace sans but...
				this.deplacementSansBut(mesParcelles);
			}
		}
	}

	/**
	 * Méthode permettant à un animal de manger une plante specifique
	 * 
	 * Pb: comment déterminer ce que va manger l'animal sur la plante (baisse de vie plante) et le gain de santé/eau?
	 * hypothèse: fonction de la santé max de mon animal (plus il a de la santé, plus il est gros, plus il mange)
	 * mais aussi de sa jauge de nourriture actuelle (plus elle est loin de la "moyenne" !!!!!!!((max+min)/2)!!!!!!, plus il mange...)
	 * Mais, depend aussi de la santé de la plante --> ne peux pas manger plus qu'il n'y a...
	 * Pb: en cas ou on tue la plante, on n'a pas de moyen de la supprimer? (on va essayer de la supprimer de la parcelle
	 * en esperant que cela marche bien par pointeurs...)
	 * idée 2: en mangeant des vegetaux, on peux egelement augmenter son hydro...
	 * ici; on met un facteur 1/2...
	 * 
	 * 
	 * @param maParcSalade la parcelle ou se trouve la plante que je vais manger
	 */
	public void mangerPlante(Parcelle maParcSalade)
	{
		Vegetaux maSalade = maParcSalade.obtientMonVegetal();
		maSalade.obtientJaugeEau();

		// Calcul de la quantité de nourriture que l'animal voudrait
		int maNourritureVoulue = this.obtientMaxNourriture() - this.obtientJaugeNourriture();

		// Calcul de la quantité maximal fournit par la plante
		int maNourritureMax = maSalade.obtientSante();

		// Determination du nombre a prendre
		if (maNourritureMax<maNourritureVoulue)
		{
			maNourritureVoulue=maNourritureMax;
		}

		//incrément jaugenourriture animal, baisse santé végétal
		this.assigneJaugeNourriture(this.obtientJaugeNourriture()+maNourritureVoulue);
		double pourcPerteEau;
		if (maSalade.obtientSante() != 0)
		{
			pourcPerteEau = maNourritureVoulue/maSalade.obtientSante();
		}
		else
		{
			pourcPerteEau=0;
		}

		maSalade.assigneSante(maSalade.obtientSante()-maNourritureVoulue);

		// calcul eau: on regarde le pourcentage de santé perdu chez la plante, on recupere ce pourcentage en eau
		// chez la plante et on ajoute la moitié chez l'animal, (sans dépasser le maximum?)
		int gainEau = (int) pourcPerteEau*maSalade.obtientJaugeEau();
		this.assigneJaugeEau(this.obtientJaugeEau()+gainEau);
		maSalade.assigneJaugeEau(maSalade.obtientJaugeEau()-gainEau);

		// suppression plante si elle est morte...

		if ((maSalade.obtientJaugeEau()<0)||maSalade.obtientSante()<0)
		{
			maParcSalade.assigneMonVegetal(null);
		}

	}

	/**
	 * Methode générale de reproduction pour un animal
	 * 
	 * @param mesParcelles les parcelles dans le champs de vision de l'animal
	 */
	public void seReproduire(ArrayList<Parcelle> mesParcelles)
	{
		// calcul des partenaires
		ArrayList<Animaux> mesPartenaires = this.rechercheMesPartenaires(mesParcelles);
		// calcul du partenaire le plus proche
		Animaux monPartenaire = this.recherchePlusProchePartenaire(mesPartenaires);
		// deplacement
		if (monPartenaire == null)
		{
			// deplacement sans aucun but...
			this.deplacementSansBut(mesParcelles);
		}
		else
		{

			if(this.obtientPosition().estContigue(monPartenaire.obtientPosition()))
			{
				// si oui --> repro
				this.copuler(this.parcellesLibresEtAccess(this.parcellesContigues(mesParcelles)));	
			}
			else
			{
				// si non --> deplacement vers son partenaire...
				this.deplacementVersPartenaire(monPartenaire, mesParcelles);
			}
		}
	}

	/** 
	 * Fonction principale de la classe Animaux: permet de faire interagir un animal avec son environnement
	 * 
	 * @param mesParcelles: l'ArrayList de parcelles à portée de vue de l'individu
	 */
	public void gererAnimaux(ArrayList<Parcelle> mesParcelles){
		// TODO: vérifier que les animaux se déplacent bien aléatoirement si ils ne font rien...

		////////////////////////////////////////////////////////
		// Calcul raison du deplacement
		int raisonDeplacement = this.calculRaisonDeplacement();
		////////////////////////////////////////////////////////

		////////////////////////////////////////////////////////
		// gestion des actions
		if ((raisonDeplacement==1)||(raisonDeplacement==3))
		{
			// CAS MANGER OU BOIRE ABSOLUMENT
			// Si dans le cas "absolument", j'ignore le predateur...
			if (raisonDeplacement==1)
			{
				// nourriture
				this.seNourrir(mesParcelles);
			}
			else if (raisonDeplacement==3)
			{
				// eau
				this.seDesalterer(mesParcelles);
			}
		}
		else
		{
			// FUITE PREDATEURS
			ArrayList<Animaux> mesPredateurs = rechercheMesPredateurs(mesParcelles);
			// Si pas dans le cas "absolument" (valeurs 1 et 3 de raisonDeplacement), on cherche a fuir le predateur
			// Selon les besoins, je calcul le deplacement...

			if (mesPredateurs.size() != 0)
			{
				// méthode de fuite de predateur
				this.fuitePredateurs(mesPredateurs, this.parcellesLibresEtAccess(mesParcelles));
			}
			else
			{
				if (raisonDeplacement != 0)
				{
					// manger ou boire
					if (raisonDeplacement==2)
					{
						// nourriture
						this.seNourrir(mesParcelles);
					}
					else if (raisonDeplacement==4)
					{
						// eau
						this.seDesalterer(mesParcelles);
					}
				}
				else
				{
					// mon animal se reproduit ou essaye
					this.seReproduire(mesParcelles);
				}
			}
		}
		//////////////////////////////////
		// Maintenant, je gère la baisse de nourriture et d'eau de mon animal
		//////////////////////////////////

		this.gestionEau();

		this.gestionNourriture();

		// je fais viellir mon animal
		this.vieillessement();
	}

	/**
	 * Méthode permettan de gerer la baisse de nourriture et la variation de santé liée
	 */
	public void gestionNourriture()
	{
		//je baisse ma nourriture
		this.baisseNourriture();
		// je diminue ma santé
		this.calculSanteNourriture();
	}

	// TODO: a revoir
	/**
	 * Méthode permettant de gerer la baisse de la jauge de nourriture
	 * PB: combien perd ede nourriture un animal?
	 * --> depend de son action
	 * PB: aucun moyen de savoir si il a bougé...
	 * Arbitrairement, on prend 2.5% de la quantité de nourriture maximale
	 */
	public void baisseNourriture()
	{
		this.jaugeNourriture = this.jaugeNourriture * this.obtientMaxNourriture()/20;
		if (this.jaugeNourriture<0)
		{
			this.assigneJaugeNourriture(0);
		}
	}

	/**
	 * Méthode permettant de calculer la variation de santé santé de la plante en fonction de la jauge de nourriture.
	 */
	public void calculSanteNourriture()
	{
		// l'entité meurt de soif :'(
		if (obtientJaugeNourriture() <= 0) {
			this.assigneSante(0);
		}

		// l'entité n'a pas assez d'eau et perd de la santée
		if (obtientJaugeNourriture() < obtientMinNourriture()) {
			this.assigneSante(obtientSante()
					- ((obtientMaxSante() / obtientSante()) * (obtientMinNourriture() - obtientJaugeNourriture()
							/ obtientMinNourriture())));
		}

		// l'entité a trop d'eau et perd de la santé
		if (obtientJaugeNourriture() > obtientMaxNourriture()) {
			this.assigneSante(obtientSante()
					- ((obtientMaxSante() / obtientSante()) * (obtientMaxNourriture() - obtientJaugeNourriture()
							/ obtientMaxNourriture())));
		}

		// l'entité a une valeur de sa jauge d'eau comprise entre min eau et max
		// eau, elle gagne une quantité de santé qui dépend de l'écart entre la
		// jauge eau et l'hydro optimale

		// hydroOptim est l'hydrométrie moyenne de l'entité i.e. la moyenne
		// entre MinEau et MaxEau
		int hydroOptim = (obtientMaxEau() + obtientMinEau() / 2);
		int nouvelleSante = (int) (obtientSante()
				/ ((Math.abs(hydroOptim - obtientJaugeEau()) + 0.0001)) + obtientSante());
		// int nouvelleSante = obtientSante()+ (1 / (Math.pow(hydroOptim -
		// obtientJaugeEau(), 2) + (1/hydroOptim)))

		if (nouvelleSante > obtientMaxSante()) {
			this.assigneSante(this.obtientMaxSante());
		}
		this.assigneSante(nouvelleSante);
	}

	/**
	 * Methode permettant un representation de mon animal sous forme de chaine de caractere
	 * 	-->	ma santé ma jauge d'eau ma jauge de nourriture est un male espece
	 * 
	 * @return l'animal sous forme de chaine de caractere
	 */
	public String toString()
	{
		String myString = new String("");

		myString+=" ma santé: "+this.obtientSante();
		myString+=" ma jauge d'eau: "+this.obtientJaugeEau();
		myString+=" ma jauge de nourriture: "+this.jaugeNourriture;
		myString+=" est un male: "+this.male;
		myString+=" espece: "+this.espece;

		return myString;
	}
}